package com.duoduo.admin.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.duoduo.common.core.constant.CacheConstants;
import com.duoduo.common.core.constant.CommonConstants;
import com.duoduo.common.core.constant.enums.MenuTypeEnum;
import com.duoduo.common.core.exception.DuoException;
import com.duoduo.common.core.exception.ErrorCode;
import com.duoduo.common.core.exception.ErrorCodes;
import com.duoduo.common.core.util.MsgUtils;
import com.duoduo.common.jpa.service.CommonService;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.setting.SettingUtil;

import com.duoduo.admin.api.dto.SysMenuDTO;
import com.duoduo.admin.entity.SysMenu;
import com.duoduo.admin.entity.SysRole;
import com.duoduo.admin.entity.SysRoleMenu;
import com.duoduo.admin.entity.SysUser;
import com.duoduo.admin.repository.SysMenuRepository;
import com.duoduo.admin.repository.SysRoleRepository;
import com.duoduo.admin.repository.SysUserRepository;

import jakarta.persistence.criteria.Predicate;
import jakarta.validation.constraints.NotNull;

@Service
public class SysMenuService extends CommonService<SysMenuDTO, SysMenu, Long>{

	@Autowired
	private SysMenuRepository sysMenuRepository;
	@Autowired
	private SysRoleRepository sysRoleRepository;
	@Autowired
	private SysUserRepository sysUserRepository;

	@Override
	public Specification<SysMenu> getWhere(final SysMenuDTO request) {
		return (root, query, cb) -> {
			final List<Predicate> predicate = new ArrayList<Predicate>();
			if(request.getId()!=null) {
				predicate.add(cb.equal(root.get("id"), request.getId()));	
			}		
			
			final Predicate[] pre = new Predicate[predicate.size()];
			return query.where(predicate.toArray(pre)).getRestriction();
		};
	}
	

	@Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId  + '_menu'", unless = "#result == null")
	public Set<SysMenu> findMenuByRoleId(Long roleId) {
		 SysRole sysRole = sysRoleRepository.findById(roleId).orElseThrow();
		 return sysRole.getPermissions();
	}
	/**
	 * 构建树查询 1. 不是懒加载情况，查询全部 2. 是懒加载，根据parentId 查询 2.1 父节点为空，则查询ID -1
	 * @param lazy 是否是懒加载
	 * @param parentId 父节点ID
	 * @return
	 */
	public List<Tree<Long>> treeMenu(boolean lazy, Long parentId) {
		if (!lazy) {
			List<SysMenu> sysMenus = sysMenuRepository.findAll(Sort.by(Direction.ASC, "sortOrder"));
			sysMenus.forEach(p -> combinePermissions(p));
			List<TreeNode<Long>> collect = sysMenus.stream()
			.filter(menu -> MenuTypeEnum.MENU.getType().equals(menu.getType())||MenuTypeEnum.IFRAME.getType().equals(menu.getType()))
			.map(getNodeFunction()).collect(Collectors.toList());
			return TreeUtil.build(collect, CommonConstants.MENU_TREE_ROOT_ID);
		}

		Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId;
		List <SysMenu>sysMenu1 = sysMenuRepository.findByParentId(parentId,Sort.by(Direction.ASC, "sortOrder"));
		sysMenu1.forEach(p -> combinePermissions(p));
		List<TreeNode<Long>> collect = sysMenu1.stream().map(getNodeFunction()).collect(Collectors.toList());
		
		return TreeUtil.build(collect, parent);
	}
	
	/**
	 * 查询菜单
	 * @param all 全部菜单
	 * @param parentId 父节点ID
	 * @return
	 */
	public List<Tree<Long>> filterMenu(Set<SysMenu> all, Long parentId) {
		List<TreeNode<Long>> collect = all.stream()
				.filter(menu -> MenuTypeEnum.MENU.getType().equals(menu.getType())||MenuTypeEnum.IFRAME.getType().equals(menu.getType()))
				.filter(menu -> StrUtil.isNotBlank(menu.getPath())).map(getNodeFunction()).collect(Collectors.toList());
		Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId;
		return TreeUtil.build(collect, parent);
	}
	/**
	 * 合并权限
	 */
	public void combinePermissions(SysMenu p) {
			List<String> permissions = new ArrayList<String>();
			Set<SysMenu> children = p.getChildren();
			if(!children.isEmpty()) {
				children.stream().filter(c -> !MenuTypeEnum.BUTTON.getType().equals(c.getType())).forEach(c -> {
					combinePermissions(c);
					permissions.addAll(Arrays.asList(c.getPermissions().split(",")));
				});
				StringBuilder per = new StringBuilder();
				permissions.forEach(e ->{
					per.append(e);
					per.append(",");
				});
				p.setPermissions(per.substring(0, per.length()-1));
			}
		
	}
	@CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true)
	public void clearMenuCache() {

	}

	/**
	 * 级联删除菜单
	 * @param id 菜单ID
	 * @return true成功, false失败
	 */
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true)
	public Boolean removeMenuById(Long id) {
		// 查询父节点为当前节点的节点
		SysMenu sysMenu = sysMenuRepository.findById(id).orElseThrow();
		sysMenuRepository.delete(sysMenu);
		return true;
	}

	
	@CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true)
	public Boolean updateMenuById(SysMenu sysMenu) {
		 sysMenuRepository.save(sysMenu);
		return true;
	}
	@NotNull
	private Function<SysMenu, TreeNode<Long>> getNodeFunction() {
		return menu -> {
			TreeNode<Long> node = new TreeNode<>();
			node.setId(menu.getId());
			node.setName(menu.getName());
			node.setParentId(menu.getParentId());
			node.setWeight(menu.getSortOrder());
			// 扩展属性
			Map<String, Object> extra = new HashMap<>();
			extra.put("icon", menu.getIcon());
			extra.put("path", menu.getPath());
			extra.put("component", menu.getComponent());
			extra.put("title", menu.getTitle());
			extra.put("redirect", menu.getRedirect());
			extra.put("type", menu.getType());
			extra.put("frameSrc", menu.getFrameSrc());
			extra.put("externalLink", menu.getExternalLink());
			extra.put("hidden", menu.getHidden());
			extra.put("disabled", menu.getDisabled());
			extra.put("permissions", StringUtils.isBlank(menu.getPermissions())?null:menu.getPermissions().split(","));
			extra.put("sortOrder", menu.getSortOrder());
			extra.put("keepAlive", menu.getKeepAlive());
			node.setExtra(extra);
			return node;
		};
	}
	@NotNull
	private Function<SysMenu, TreeNode<Long>> getNodeFunctionDTO() {
		return menu -> {
			TreeNode<Long> node = new TreeNode<>();
			node.setId(menu.getId());
			node.setName(menu.getName());
			node.setParentId(menu.getParentId());
			node.setWeight(menu.getSortOrder());
			// 扩展属性
			Map<String, Object> extra = new HashMap<>();
			
			extra.put("icon", menu.getIcon());
			extra.put("path", menu.getPath());
			extra.put("component", menu.getComponent());
			extra.put("title", menu.getTitle());
			extra.put("redirect", menu.getRedirect());
			extra.put("type", menu.getType());
			extra.put("frameSrc", menu.getFrameSrc());
			extra.put("externalLink", menu.getExternalLink());
			extra.put("hidden", menu.getHidden());
			extra.put("disabled", menu.getDisabled());
			extra.put("permissions", menu.getPermissions());
			extra.put("sortOrder", menu.getSortOrder());
			extra.put("keepAlive", menu.getKeepAlive());
			node.setExtra(extra);
			return node;
		};
	}
	public List<Tree<Long>> userMenu(Long id) {
		Set<SysMenu> menus = new HashSet<SysMenu>();
		SysUser sysUser = sysUserRepository.findById(id).orElseThrow(() ->{
			return new DuoException(ErrorCode.NOT_FIND);
		});
		Set<SysRole> roules = sysUser.getAuthorities();
		roules.forEach(p ->{
			menus.addAll(p.getPermissions());
		});
		
		return filterMenu(menus,null);
	}


	public List<Tree<Long>> list() {
		List<SysMenu> sysMenus = sysMenuRepository.findAll(Sort.by(Direction.ASC, "sortOrder"));
		List<TreeNode<Long>> collect = sysMenus.stream()
				.filter(menu -> StrUtil.isNotBlank(menu.getPath())).map(getNodeFunctionDTO()).collect(Collectors.toList());
		return TreeUtil.build(collect, CommonConstants.MENU_TREE_ROOT_ID);
	}

}
